﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XMaze3D
{
    public enum Direction
    {
        dirNORTH = 0,
        dirEAST,
        dirSOUTH,
        dirWEST,
        FLOOR
    };

    public class MazeCell
    {
        public bool northWallPresent, eastWallPresent, southWallPresent, westWallPresent;
        public MazeCell()
        {
            northWallPresent = eastWallPresent = southWallPresent = westWallPresent = true;
        }
    }

    public class Maze
    {
        bool[] wallsExist = new bool[4];
        public int rows, cols;

        MazeCell[,] maze;

        DisjointSet mazeSet;

        public Maze()
            : this(4, 4)
        { }

        public Maze(int rows, int cols)
        {
            this.rows = rows;
            this.cols = cols;
            mazeSet = new DisjointSet(rows * cols);
            maze = new MazeCell[rows, cols];

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    maze[i, j] = new MazeCell();
                }
            }
        }

        public void Create()
        {
            maze[0, 0].northWallPresent = false; //entrance
            maze[rows - 1, cols - 1].southWallPresent = false; //exit

            Random rand = new Random();

            int entrySet = mazeSet.Find(0);
            int exitSet = mazeSet.Find((rows * cols) - 1);

            while (entrySet != exitSet)
            {
                int randRow = rand.Next(rows);
                int randCol = rand.Next(cols);
                int wall = rand.Next(4);
                int s1 = 0;
                int s2 = 0;

                switch ((Direction)wall)
                {
                    case Direction.dirNORTH:
                        if (randRow > 0)
                        { // not top of maze
                            s1 = mazeSet.Find(cols * randRow + randCol);
                            s2 = mazeSet.Find(cols * (randRow - 1) + randCol);
                            if (s1 != s2)
                            {
                                maze[randRow, randCol].northWallPresent = false;
                                maze[randRow - 1, randCol].southWallPresent = false;
                                mazeSet.UnionSets(s1, s2);
                            }
                        }
                        break;

                    case Direction.dirSOUTH:
                        if (randRow < (rows - 1))
                        { // not bottom of maze
                            s1 = mazeSet.Find(cols * randRow + randCol);
                            s2 = mazeSet.Find(cols * (randRow + 1) + randCol);
                            if (s1 != s2)
                            {
                                maze[randRow, randCol].southWallPresent = false;
                                maze[randRow + 1, randCol].northWallPresent = false;
                                mazeSet.UnionSets(s1, s2);
                            }
                        }
                        break;

                    case Direction.dirEAST:
                        if (randCol < (cols - 1))
                        { // not right of maze
                            s1 = mazeSet.Find(cols * randRow + randCol);
                            s2 = mazeSet.Find(cols * randRow + randCol + 1);
                            if (s1 != s2)
                            {
                                maze[randRow, randCol].eastWallPresent = false;
                                maze[randRow, randCol + 1].westWallPresent = false;
                                mazeSet.UnionSets(s1, s2);
                            }
                        }
                        break;
                    case Direction.dirWEST:
                        if (randCol > 0)
                        { // not left of maze
                            s1 = mazeSet.Find(cols * randRow + randCol);
                            s2 = mazeSet.Find(cols * randRow + randCol - 1);
                            if (s1 != s2)
                            {
                                maze[randRow, randCol].westWallPresent = false;
                                maze[randRow, randCol - 1].eastWallPresent = false;
                                mazeSet.UnionSets(s1, s2);
                            }
                        }
                        break;
                }

                entrySet = mazeSet.Find(0);
                exitSet = mazeSet.Find((rows * cols) - 1);
            }
            return;
        }

        public MazeCell GetCell(int r, int c)
        {
            if (r < 0 || r >= rows || c < 0 || c >= cols)
                return null;
            else
                return maze[r, c];

        }
    }
}
